/*
* ====================================================================
* Copyright (c) 2013-2014 Neudesic.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that redistributions of source
* retain the above copyright notice, this list of conditions and the
* following disclaimer.
*
* This source file includes software written by Neudesic, LLC (http://www.neudesic.com).
*
* THIS SOFTWARE IS PROVIDED BY Neudesic, LLC ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  FURTHER, IN NO EVENT SHALL
* Neudesic, LLC (THE AUTHOR) OR ANY CONTRIBUTORS BE LIABLE FOR ANY DAMAGES
* WHATSOEVER THAT ARE RELATED TO THE SOFTWARE [INCLUDING BUT NOT LIMITED TO,
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* SUCH AS LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION, HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)] EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGES.
*
* ====================================================================
*/
#include "winrtsock.h"

// Only include these functions if we're running in WindowsRT (Windows Store app)
#if UNDER_WINDOWS_RT_METRO

#include "ISocketContainer.h"
#include "StreamSocketContainer.h"
#include "DatagramSocketContainer.h"
#include <string.h>
#include <vector>
#include <Windows.Foundation.h>
#include <windows.networking.h>
#include <windows.networking.connectivity.h>
#include <wrl/wrappers/corewrappers.h>
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <windows.networking.sockets.h>
#include <windows.system.threading.h>
#include <wrl/client.h>
#include <wrl/event.h>
#include <synchapi.h>
#include <sstream>
#include "winrtutils.h"

using namespace Neudesic::Sockets;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Foundation::Collections;
using namespace ABI::Windows::Networking;
using namespace ABI::Windows::Networking::Connectivity;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using ABI::Windows::Networking::HostNameType;

using namespace ABI::Windows::Storage::Streams;
using namespace ABI::Windows::Networking::Sockets;
using namespace Neudesic::Sockets;

static std::vector<std::string> GetHostNames(HostNameType type);

#pragma region Global replacements for WSAGetLastError, WSASetLastError
//Since we can't use WSAGetLastError, setup our own mechanism.
__declspec(thread) long tlslasterror = 0;
long WSAGetLastError()
{
	return tlslasterror;
}

void WSASetLastError(long error)
{
	tlslasterror = error;
}

#pragma endregion Global replacements for WSAGetLastError, WSASetLastError

#pragma region Utility functions
//WinRT does not support Sleep. This is a simulation using an event object. Since the event object
//never signals, it waits until it times out effectively sleeping for the required period.
void Sleep(UINT timeout)
{
	AutoResetEvent timeKill;
	timeKill.Wait(timeout);
	timeKill.Close();
}

USHORT htons(USHORT hostshort)
{
	return ((hostshort & 0xff00) >> 8) | ((hostshort & 0x00ff) << 8);
}

ULONG htonl(ULONG hostlong)
{
	return (((hostlong & 0xff000000) >> 24) | ((hostlong & 0x00ff0000) >> 8) |
		((hostlong & 0x0000ff00) << 8) | ((hostlong & 0x000000ff) << 24));
}

ULONG ntohl(ULONG netlong)
{
	return htonl(netlong);
}

USHORT ntohs(USHORT hostshort)
{
	return htons(hostshort);
}

#pragma endregion Utility fuctions

#pragma region Misc statics

static int GetNetworkAdapterIanaInterfaceType(const ComPtr<INetworkAdapter>& networkAdapter)
{
	unsigned int adapterIanaInterfaceType;
	HRESULT hr = networkAdapter->get_IanaInterfaceType(&adapterIanaInterfaceType);

	return SUCCEEDED(hr) ? adapterIanaInterfaceType : -1;
}

static int GetIpInformationIanaInterfaceType(const ComPtr<IIPInformation>& ipInformation)
{
	ComPtr<INetworkAdapter> networkAdapter;
	HRESULT hr = ipInformation->get_NetworkAdapter(&networkAdapter);
	return SUCCEEDED(hr) ? GetNetworkAdapterIanaInterfaceType(networkAdapter) : -1;
}

static GUID GetNetworkAdapterNetworkId(const ComPtr<INetworkAdapter>& networkAdapter)
{
	GUID networkAdapterId;
	HRESULT hr = networkAdapter->get_NetworkAdapterId(&networkAdapterId);
	if (FAILED(hr))
		networkAdapterId.Data1 = 0xffffffff;
	return networkAdapterId;
}

static GUID GetIpInformationNetworkId(const ComPtr<IIPInformation>& ipInformation)
{
	ComPtr<INetworkAdapter> networkAdapter;
	HRESULT hr = ipInformation->get_NetworkAdapter(&networkAdapter);

	GUID networkAdapterId;
	if (SUCCEEDED(hr))
		hr = networkAdapter->get_NetworkAdapterId(&networkAdapterId);
	if (FAILED(hr))
		networkAdapterId.Data1 = 0xaaaaaaaa;
	return networkAdapterId;
}

static std::vector<std::string> GetHostNames(HostNameType type)
{
	std::vector<std::string> foundHosts;
	ComPtr<ABI::Windows::Networking::Connectivity::INetworkInformationStatics> networkStatics;
	HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Networking_Connectivity_NetworkInformation).Get(), &networkStatics);
	if (SUCCEEDED(hr))
	{
		ABI::Windows::Foundation::Collections::IVectorView<ABI::Windows::Networking::HostName*>* hostNames;
		hr = networkStatics->GetHostNames(&hostNames);
		if (SUCCEEDED(hr))
		{
			UINT size;
			hostNames->get_Size(&size);
			for (unsigned int i = 0; i < size; i++)
			{
				ComPtr<ABI::Windows::Networking::IHostName> hostName;
				hostNames->GetAt(i, &hostName);
				ABI::Windows::Networking::HostNameType hostNameType;
				hostName->get_Type(&hostNameType);
				if (hostNameType == type)
				{
					HSTRING canonicalName;
					hostName->get_CanonicalName(&canonicalName);
					UINT len;
					std::wstring wname = std::wstring(::WindowsGetStringRawBuffer(canonicalName, &len));
					::WindowsDeleteString(canonicalName);
					foundHosts.push_back(ConvertToMultiByteString(wname));
				}
			}
		}
	}
	return foundHosts;
}

#pragma endregion Misc statics

#pragma region closesocket, ioctlsocket, setsockopt, shutdown, WSAStartup, WSAEventSelect, WSAEnumNetworkEvents, WSAWaitForMultipleEvents, WSACreateEventEx, WSASetEvent

int ioctlsocket(SOCKET socket, long command, ULONG *argp)
{
	ISocketContainer* container = reinterpret_cast<ISocketContainer*>(socket);
	return container->GetUnconsumedBufferLength(command, argp);
}

int setsockopt(SOCKET socket, int level, int optname, const char* optval, int optlen)
{
	ISocketContainer* container = reinterpret_cast<ISocketContainer*>(socket);
	return container->SetSocketOptions(level, optname, optval, optlen);
}

int shutdown(SOCKET socket, int how)
{
	ISocketContainer* container = reinterpret_cast<ISocketContainer*>(socket);
	int result = container->Shutdown(how);
	if (SocketType::DatagramSocketType == container->GetSocketType())
		delete (DatagramSocketContainer*)container;
	else
		delete (StreamSocketContainer*)container;
	return result;
}

int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData)
{
	::ZeroMemory(lpWSAData, sizeof(WSADATA));
	lpWSAData->wVersion = wVersionRequested;
	lpWSAData->wHighVersion = wVersionRequested;
	char description[] = "WinRtSock 1.0";
	char systemStatus[] = "Running";
	memcpy_s(lpWSAData->szDescription, WSADESCRIPTION_LEN, description, sizeof(description));
	memcpy_s(lpWSAData->szSystemStatus, WSASYS_STATUS_LEN, systemStatus, sizeof(systemStatus));
	::WSASetLastError(0);
	return 0;
}

int WSACleanup()
{
	return 0;
}

int WSAEventSelect(SOCKET socket, WSAEVENT hEventObject, long lNetworkEvents)
{
	ISocketContainer* container = reinterpret_cast<ISocketContainer*>(socket);
	return container->EventSelect(hEventObject, lNetworkEvents);
}

int WSASetEvent(WSAEVENT hEvent)
{
	//We don't have to go through the socket container. Just signal the event. The event is reset
	//automatically when WSAEnumNetworkEvents is called.
	return ::SetEvent(hEvent);
}

//Unlike the winsock version of this function, we need the container to which to associate this event.
WSAEVENT WSACreateEventEx(SOCKET socket)
{
	ManualResetEvent wsaEvent;
	ISocketContainer* container = reinterpret_cast<ISocketContainer*>(socket);
	HRESULT hr = container->SetWSAEvent(wsaEvent.Get());
	return SUCCEEDED(hr) ? wsaEvent.Detach() : (HANDLE)hr;
}

int WSACloseEvent(HANDLE hEvent)
{
	//This object is not owned by the container. We must close it.
	::CloseHandle(hEvent);
	::WSASetLastError(S_OK);
	return S_OK;
}

int WSAWaitForMultipleEvents(DWORD cEvents, const WSAEVENT *lphEvents, BOOL fWaitAll, DWORD dwTimeout, BOOL fAlertable)
{
	return ::WaitForMultipleObjectsEx(cEvents, lphEvents, fWaitAll, dwTimeout, fAlertable);
}

int WSAEnumNetworkEvents(SOCKET socket, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents)
{
	ISocketContainer* container = reinterpret_cast<ISocketContainer*>(socket);
	return container->GetNetworkEvents(hEventObject, lpNetworkEvents);
}

#pragma endregion ioctlsocket, shutdown, WSAStartup, WSAEventSelect, WSAEnumNetworkEvents, WSAWaitForMultipleEvents, WSACreateEventEx, WSASetEvent

#pragma region getaddrinfo, gethostbyaddr, gethostname, getnameinfo, getpeername, getsockname, freeaddrinfo, getservbyname
int Build_getaddrinfoResults(
	ABI::Windows::Foundation::IAsyncOperation<__FIVectorView_1_Windows__CNetworking__CEndpointPair*>* operation,
	const char* service, const struct addrinfo *hints, struct addrinfo **results)
{
	ABI::Windows::Foundation::Collections::IVectorView<ABI::Windows::Networking::EndpointPair*> *endpointPairs;
	HRESULT hr = operation->GetResults(&endpointPairs);
	if (::ContinueIfSucceeded(hr, L"Failed to get EndpointPairs results."))
	{
		*results = (addrinfo*)::malloc(sizeof(addrinfo));
		addrinfo* pai = *results;
		::ZeroMemory(pai, sizeof(addrinfo));
		UINT size;
		endpointPairs->get_Size(&size);
		bool addlink = false;
		for (unsigned int i = 0; i < size; i++)
		{
			if (addlink)
			{
				pai->ai_next = (addrinfo*)::malloc(sizeof(addrinfo));
				::ZeroMemory(pai->ai_next, sizeof(addrinfo));
				pai = pai->ai_next;
				addlink = false;
			}

			ComPtr<ABI::Windows::Networking::IEndpointPair> ep;
			endpointPairs->GetAt(i, &ep);
			ComPtr<IHostName> remoteHostName;
			ep->get_RemoteHostName(&remoteHostName);
			HSTRING remoteServiceName;
			ep->get_RemoteServiceName(&remoteServiceName);
			HSTRING remoteCanonicalName;
			remoteHostName->get_CanonicalName(&remoteCanonicalName);
			std::string mbServiceName = ::ConvertToMultiByteString(&remoteServiceName);
			std::string mbCanonicalName = ::ConvertToMultiByteString(&remoteCanonicalName);
			::WindowsDeleteString(remoteServiceName);
			::WindowsDeleteString(remoteCanonicalName);

			in_addr ia4;
			in_addr6 ia6;
			::ZeroMemory(&ia4, sizeof(in_addr));
			::ZeroMemory(&ia6, sizeof(in_addr6));
			//First determine the type of address that we have.
			BOOL ipv6 = ::inet_pton(AF_INET6, mbCanonicalName.c_str(), &ia6);
			BOOL ipv4 = ipv6 ? 0 : ::inet_pton(AF_INET, mbCanonicalName.c_str(), &ia4);
			if (!ipv4 && !ipv6)
				ipv4 = ::inet_pton(AF_INET, "0.0.0.0", &ia4);
			int family = ipv6 ? AF_INET6 : AF_INET;		//Defaults to AF_INET

			if (hints)	//If hints are provided, be selective
			{
				//If the family is unspecified, we capture them all.
				if (hints->ai_family != AF_UNSPEC)
				{
					if (ipv6)
					{
						//This first check is unusual. I discovered that the family could be wrong. Even
						//if wrong, we have to allow it through or we may end up with an empty addrinfo list.
						if (hints->ai_family != AF_INET && hints->ai_family != AF_INET6)
							continue;
					}
					else if (ipv4)
					{
						//This first check is unusual. I discovered that the family could be wrong. Even
						//if wrong, we have to allow it through or we may end up with an empty addrinfo list.
						if (hints->ai_family != AF_INET6 && hints->ai_family != AF_INET)
							continue;
					}
				}
				//Since we cannot determine these values, use whatever was requested in the hints.
				pai->ai_socktype = hints->ai_socktype;
				pai->ai_protocol = hints->ai_protocol;
			}
			else
			{
				//These next two values are hardcoded. There does not appear to be a way to get these values using WinRT, at least that I've found.
				pai->ai_socktype = SOCK_DGRAM;
				pai->ai_protocol = IPPROTO_UDP;
			}

			addlink = true;							//We need to add a link
			pai->ai_family = family;

			if (AF_INET6 == pai->ai_family)
			{
				pai->ai_addr = (sockaddr*)::malloc(sizeof(sockaddr_in6));
				sockaddr_in6* psa = (sockaddr_in6*)pai->ai_addr;
				::ZeroMemory(psa, sizeof(sockaddr_in6));
				//Convert port to network byte order which is big endian.
				psa->sin6_port = ::htons((USHORT)atoi(mbServiceName.c_str()));
				psa->sin6_family = pai->ai_family;
				pai->ai_addrlen = sizeof(struct sockaddr_in6);
				::memcpy_s(&psa->sin6_addr, sizeof(in_addr6), &ia6, sizeof(in_addr6));
			}
			else
			{
				pai->ai_addr = (sockaddr*)::malloc(sizeof(sockaddr_in));
				sockaddr_in* psa = (sockaddr_in*)pai->ai_addr;
				::ZeroMemory(psa, sizeof(sockaddr_in));
				//Convert port to network byte order which is big endian.
				psa->sin_port = ::htons((USHORT)atoi(mbServiceName.c_str()));
				psa->sin_family = pai->ai_family;
				pai->ai_addrlen = sizeof(struct sockaddr_in);
				::memcpy_s(&psa->sin_addr, sizeof(in_addr), &ia4, sizeof(in_addr));
			}
		}
	}

	return hr;
}

int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **results)
{
	ComPtr<ABI::Windows::Networking::Sockets::IDatagramSocketStatics> datagramStatics;
	HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Networking_Sockets_DatagramSocket).Get(), &datagramStatics);
	if (::ContinueIfSucceeded(hr, L"Failed to get DatagramSocketStatics."))
	{
		ComPtr<IHostNameFactory> hostNameFactory;
		hr = ABI::Windows::Foundation::GetActivationFactory(HStringReference(RuntimeClass_Windows_Networking_HostName).Get(), &hostNameFactory);
		if (::ContinueIfSucceeded(hr, L"Failed to get HostNameFactory."))
		{
			char* localName;
			if (!node)
				localName = (hints && hints->ai_family == AF_INET6) ? "::1" : "0.0.0.0";
			else if (strcmp("::", node) == 0)
				localName = "::1";
			else
				localName = const_cast<char*>(node);

			wchar_t wName[NI_MAXHOST];
			::ConvertToWideString(localName, (sizeof(wName) / sizeof(wchar_t)), wName);
			HSTRING hHostName;
			::WindowsCreateString(wName, ::wcslen(wName), &hHostName);
			ComPtr<IHostName> remoteHostName;
			hr = hostNameFactory->CreateHostName(hHostName, &remoteHostName);
			::WindowsDeleteString(hHostName);
			if (::ContinueIfSucceeded(hr, L"Failed to create HostName from factory."))
			{
				::ConvertToWideString(service, (sizeof(wName) / sizeof(wchar_t)), wName);
				HSTRING hRemoteServiceName;
				::WindowsCreateString(wName, ::wcslen(wName), &hRemoteServiceName);

				HANDLE hEvent = ::CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);
				auto getEndpointPairsCompletedHandler = ::Callback<ABI::Windows::Foundation::IAsyncOperationCompletedHandler<ABI::Windows::Foundation::Collections::IVectorView<ABI::Windows::Networking::EndpointPair*> *>>(
					[&hEvent](ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Foundation::Collections::IVectorView<ABI::Windows::Networking::EndpointPair*>*> * ep, AsyncStatus asyncStatus)
				{
					::SetEvent(hEvent);
					return S_OK;
				});
				ABI::Windows::Foundation::__FIAsyncOperation_1___FIVectorView_1_Windows__CNetworking__CEndpointPair_t* operation;
				//ABI::Windows::Foundation::IAsyncOperation<IVectorView<ABI::Windows::Networking::EndpointPair*>> operation;
				hr = datagramStatics->GetEndpointPairsAsync(remoteHostName.Get(), hRemoteServiceName, &operation);
				::WindowsDeleteString(hRemoteServiceName);
				if (::ContinueIfSucceeded(hr, L"Failed to get EndpointPairs."))
				{
					hr = operation->put_Completed(getEndpointPairsCompletedHandler.Get());
					if (::ContinueIfSucceeded(hr, L"Failed put_Completed."))
					{
						DWORD result = ::WaitForSingleObjectEx(hEvent, TimeoutDuration::EndpointPairs, false);
						if (WAIT_OBJECT_0 != result)
							::ShowErrorMessage(L"Waiting to get EndpointPairs returned %d/0x%x", result, result);
					}
					if (SUCCEEDED(hr))
					{
						hr = Build_getaddrinfoResults(operation, service, hints, results);
					}
				}
				::CloseHandle(hEvent);
			}
		}
	}

	if (FAILED(hr))
		::freeaddrinfo(*results);
	::WSASetLastError(FAILED(hr) ? WSAEFAULT : 0);
	return hr;
}

void freeaddrinfo(struct addrinfo* ai)
{
	if (!ai)
		return;
	for (struct addrinfo *ptr = ai; ptr; ptr = ptr->ai_next)
	{
		if (ptr->ai_next)
			::freeaddrinfo(ptr->ai_next);
		::free(ptr->ai_addr);
		::free(ptr->ai_canonname);
		::free(ptr);
		break;
	}
	//::free(ai);
}

struct hostent* gethostbyaddr(const char *addr, int length, int type)
{
	char strIp[NI_MAXHOST];
	char strPort[NI_MAXSERV];
	hostent* hostentry = nullptr;
	sockaddr socka;
	::ZeroMemory(&socka, sizeof(sockaddr));
	socka.sa_family = (ADDRESS_FAMILY)type;
	memcpy_s(socka.sa_data, sizeof(socka.sa_data), addr, length);
	HRESULT hr = getnameinfo(&socka, sizeof(sockaddr), strIp, NI_MAXHOST, strPort, NI_MAXSERV, 0);
	if (SUCCEEDED(hr))
	{
		hostent* hostentry = (hostent*)::malloc(sizeof(hostent));
		::ZeroMemory(hostentry, sizeof(hostent));
		int len = ::strlen(strIp);
		hostentry->h_name = (char*)::malloc(len + 1);
		::memcpy_s(hostentry->h_name, len, strIp, len);
		hostentry->h_addrtype = type;
	}
	WSASetLastError(hr);
	return hostentry;
}

int gethostname(char* name, int length)
{
	std::vector<std::string> foundHosts = ::GetHostNames(HostNameType_DomainName);
	if (foundHosts.size() > 0)
	{
		std::string foundHost = foundHosts.at(0);
		if (foundHosts.size() > 1)
		{
			for (auto iter = foundHosts.begin(); iter != foundHosts.end(); iter++)
			{
				foundHost = *iter;
				std::string tmp;
				for (UINT i = 0; i < foundHost.length(); i++)
				{
					if (foundHost.at(i) == '.')
						break;
					tmp += foundHost.at(i);
				}
				tmp += ".local";
				if (::_stricmp(tmp.c_str(), foundHost.c_str()) != 0)
					break;
			}
		}
		int len = min(foundHost.length() + 1, (UINT)length);
		::strncpy_s(name, len, foundHost.c_str(), len);
	}

	::WSASetLastError(S_OK);
	return 0;
}

int getsockname(SOCKET socket, struct sockaddr *name, int* namelen)
{
	ISocketContainer* socketContainer = reinterpret_cast<ISocketContainer*>(socket);
	return socketContainer->GetSocketName(name, namelen);
}

int getpeername(SOCKET socket, struct sockaddr *name, int* namelen)
{
	ISocketContainer* socketContainer = reinterpret_cast<ISocketContainer*>(socket);
	return socketContainer->GetPeerName(name, namelen);
}

int getnameinfo(const struct sockaddr *sa, socklen_t salen, char* host, socklen_t hostlen, char* service, socklen_t servicelen, int flags)
{
	//Unfortunately, there is no equivalent of getnameinfo in WinRT at this time. We'll just have to make do.

	// Note: this is only called in two places, and both with the NI_NUMERICHOST flag, so it's safe to only handle that case.
	HRESULT hr = S_OK;

	std::string sPort;
	if (sa->sa_family == AF_INET)
	{
		sockaddr_in* sa4 = (sockaddr_in*)sa;
		inet_ntop(AF_INET, (const void*)&sa4->sin_addr, host, hostlen);

		//Network format is big endian. Need to convert back to little endian.
		sPort = std::to_string(::ntohs(sa4->sin_port));
	}
	else if (sa->sa_family == AF_INET6)
	{
		sockaddr_in6* sa6 = (sockaddr_in6*)sa;
		inet_ntop(AF_INET6, (const void*)&sa6->sin6_addr, host, hostlen);

		//Network format is big endian. Need to convert back to little endian.
		sPort = std::to_string(::ntohs(sa6->sin6_port));
	}
	else
	{
		hr = E_FAIL;
	}
	if (SUCCEEDED(hr))
	{
		auto cPort = sPort.c_str();
		if (servicelen && service)
			::strcpy_s(service, servicelen, cPort);
	}
	::WSASetLastError(FAILED(hr) ? WSAEFAULT : 0);
	return hr;
}

struct servent* getservbyname(const char *name, const char *proto)
{
	return nullptr;
}

struct hostent* gethostbyname(const char *name)
{
	addrinfo addr;
	addrinfo* paddr = &addr;

	struct addrinfo hints;
	::ZeroMemory(&hints, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;

	getaddrinfo(name, "0", &hints, &paddr);
	hostent* phostent = (hostent*)::malloc(sizeof(hostent));
	phostent->h_addrtype = addr.ai_family;
	phostent->h_length = 4;	//need to fix
	if (nullptr != addr.ai_canonname)
	{
		phostent->h_name = (char*)::malloc(::strlen(addr.ai_canonname));
	}
	return phostent;
}

#pragma endregion getaddrinfo, gethostbyaddr, gethostname, getnameinfo, getpeername, getsockname, freeaddrinfo, getservbyname

#pragma region accept, socket, connect, WSAConnect, select, bind, closesocket, getsockopt, WSACancelBlockingCall
SOCKET accept(SOCKET socket, struct sockaddr *addr, int *addrlen)
{
	return ::WSAAccept(socket, addr, addrlen, nullptr, 0);
}

void WSACancelBlockingCall()
{
	//All WinRT calls are non-blocking
}

SOCKET socket(int family, int type, int protocol)
{
	int error = 0;
	ISocketContainer* container = nullptr;
	switch (type)
	{
	case SOCK_STREAM:
		container = reinterpret_cast<ISocketContainer*>(new StreamSocketContainer());
		break;
	case SOCK_DGRAM:
		container = reinterpret_cast<ISocketContainer*>(new DatagramSocketContainer());
		break;
	default:
		error = WSAESOCKTNOSUPPORT;
		break;
	}
	if (!error)
	{
		if (!container->IsRunning())
		{
			closesocket((SOCKET)container);
			container = nullptr;
			error = WSAEPROVIDERFAILEDINIT;
		}
	}
	//::ShowInfoMessage(L"Created container [%d].", container);
	::WSASetLastError(error);
	return !error ? (SOCKET)container : (SOCKET)INVALID_SOCKET;
}

int connect(SOCKET socket, const struct sockaddr* name, int namelen)
{
	ISocketContainer* container = reinterpret_cast<ISocketContainer*>(socket);
	HRESULT hr = container->Connect(name, namelen);
	//In winsock connect always returns WSAEWOULDBLOCK. This forces the underlying library to do a select.
	if (SUCCEEDED(hr))
	{
		::WSASetLastError(WSAEWOULDBLOCK);
		hr = SOCKET_ERROR;
	}
	return hr;
}

int WSAConnect(SOCKET socket, const struct sockaddr *name, int namelen, LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS)
{
	//WSAConnect works a little differently than connect with how Doubango uses it. Winsock connect
	//always returns WSAEWOULDBLOCK. Per the documentation so does WSAConnect. Doubango, although it
	//handles it, displays an error if WSAConnect returns any error. This version of connect will
	//wait for the connect to complete instead of returning WSAEWOULDBLOCK.
	ISocketContainer* container = reinterpret_cast<ISocketContainer*>(socket);
	return container->Connect(name, namelen, lpCallerData, lpCalleeData, lpSQOS, lpGQOS);
}

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout)
{
	return StreamSocketContainer::Select(readfds, writefds, exceptfds, timeout);
}

int bind(SOCKET socket, const struct sockaddr* name, int namelen)
{
	ISocketContainer* socketContainer = reinterpret_cast<ISocketContainer*>(socket);
	int result = 0;
	switch (socketContainer->GetSocketType())
	{
	case SocketType::DatagramSocketType:
		result = ((DatagramSocketContainer*)socketContainer)->BindSocket(name);
		break;
	case SocketType::StreamSocketType:
		//On WinRT, StreamSockets don't have a bind unless using a StreamSocketListener.
		break;
	default:
		result = INVALID_SOCKET;
	}
	::WSASetLastError((result < 0) ? WSAEFAULT : 0);
	return result;
}

int closesocket(SOCKET socket)
{
	ISocketContainer* container = reinterpret_cast<ISocketContainer*>(socket);
	//::ShowInfoMessage(L"Closing container [%d].", container);
	container->Close();
	if (SocketType::DatagramSocketType == container->GetSocketType())
		delete (DatagramSocketContainer*)container;
	else
		delete (StreamSocketContainer*)container;
	return S_OK;
}

int getsockopt(SOCKET socket, int level, int optname, char *optval, int *optlen)
{
	//ISocketContainer* socketContainer = reinterpret_cast<ISocketContainer*>(socket);
	//return (SocketType::StreamSocketType == socketContainer->GetSocketType())
	//	? GetStreamSocketOptions(socket, level, optname, optval, optlen)
	//	: GetDatagramSocketOptions(socket, level, optname, optval, optlen);
	return -1;
}

#pragma endregion socket, connect, WSAConnect, select, bind, closesocket, getsockopt, WSACancelBlockingCall

#pragma region recv, recvfrom, WSARecv, WSARecvFrom, listen, WSAAccept
int listen(SOCKET socket, int backlog)
{
	StreamSocketContainer* container = reinterpret_cast<StreamSocketContainer*>(socket);
	return container->Listen(backlog);
}

SOCKET WSAAccept(SOCKET socket, struct sockaddr* addr, LPINT addrlen, LPCONDITIONPROC lpfnCondition, DWORD_PTR dwCallbackData)
{
	StreamSocketContainer* container = reinterpret_cast<StreamSocketContainer*>(socket);
	ISocketContainer* newSocket = container->GetAcceptedSocket(addr, addrlen, lpfnCondition, dwCallbackData);
	SOCKET acceptedSocket = newSocket ? reinterpret_cast<SOCKET>(newSocket) : INVALID_SOCKET;
	::WSASetLastError((INVALID_SOCKET == acceptedSocket) ? WSAEFAULT : 0);
	return acceptedSocket;
}


int recv(SOCKET socket, char *buf, int len, int flags)
{
	return ::recvex(socket, buf, len, flags, nullptr);
}

DllExport int recvex(SOCKET socket, char *buf, int len, int flags, LPDWORD lpNumberOfBytesRecvd)
{
	StreamSocketContainer* container = reinterpret_cast<StreamSocketContainer*>(socket);
	return container->Receive(buf, len, flags, lpNumberOfBytesRecvd);
}

int recvfrom(SOCKET socket, char *buf, int len, int flags, struct sockaddr *from, int *fromlen)
{
	return ::recvfromex(socket, buf, len, flags, from, fromlen, nullptr);
}

DllExport int recvfromex(SOCKET socket, char *buf, int len, int flags, struct sockaddr *from, int *fromlen, LPDWORD lpNumberOfBytesRecvd)
{
	DatagramSocketContainer* container = reinterpret_cast<DatagramSocketContainer*>(socket);
	return container->ReceiveFrom(buf, len, flags, from, fromlen, lpNumberOfBytesRecvd);
}

int WSARecv(SOCKET socket, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
	HRESULT hr = E_FAIL;
	if (1 == dwBufferCount && nullptr == lpOverlapped && nullptr == lpCompletionRoutine)
	{
		if (nullptr != lpBuffers)
			hr = ::recvex(socket, lpBuffers->buf, lpBuffers->len, *lpFlags, lpNumberOfBytesRecvd);
	}
	::WSASetLastError(FAILED(hr) ? WSAEFAULT : 0);
	return hr;
}

int WSARecvFrom(SOCKET socket, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, struct sockaddr *lpFrom, LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
	return recvfromex(socket, lpBuffers->buf, lpBuffers->len, *lpFlags, lpFrom, lpFromlen, lpNumberOfBytesRecvd);
}

#pragma endregion  recv, recvfrom, WSARecv, WSARecvFrom, listen, WSAAccept

#pragma region send, sendto, WSASend, WSASendTo
int send(SOCKET socket, const char* buf, unsigned int len, int flags)
{
	StreamSocketContainer* container = reinterpret_cast<StreamSocketContainer*>(socket);
	if (container->GetSocketType() != SocketType::StreamSocketType)
	{
		::ShowInfoMessage(L"Attempt to execute send failed. StreamSocketContainer expected, found [%s]", container->GetSocketType());
		return E_FAIL;
	}

	UINT32 numberOfBytesSent = 0;
	HRESULT result = container->Send(buf, len, flags, &numberOfBytesSent);
	return numberOfBytesSent;
}

int sendto(SOCKET socket, const char* buf, unsigned int len, int flags, const sockaddr* to, int tolen)
{
	DatagramSocketContainer* container = reinterpret_cast<DatagramSocketContainer*>(socket);
	if (container->GetSocketType() != SocketType::DatagramSocketType)
	{
		::ShowInfoMessage(L"Attempt to execute sendto failed. DatagramSocketContainer expected, found [%s]", container->GetSocketType());
		return E_FAIL;
	}
	UINT32 numberOfBytesSent = 0;
	HRESULT hr = container->SendTo(buf, len, flags, to, tolen, &numberOfBytesSent);
	return (FAILED(hr)) ? hr : numberOfBytesSent;
}

int WSASend(SOCKET socket, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags,
	LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
	ULONG totalBytesSent = 0;
	ULONG totalBytesToSend = 0;
	LPWSABUF wsaBuffer = lpBuffers;
	for (DWORD i = 0; i < dwBufferCount; i++)
	{
		int numberOfBytesSent = send(socket, wsaBuffer->buf, wsaBuffer->len, dwFlags);
		if (numberOfBytesSent != wsaBuffer->len)
		{
			ISocketContainer* container = reinterpret_cast<ISocketContainer*>(socket);
			::ShowInfoMessage(L"WSASend:Number of bytes to be sent [%d]. Reportedly sent [%d] for container [%d].",
				wsaBuffer->len, numberOfBytesSent, container);
		}
		totalBytesSent += numberOfBytesSent;
		totalBytesToSend += wsaBuffer->len;
		wsaBuffer += wsaBuffer->len + sizeof(wsaBuffer->len);
	}
	if (nullptr != lpNumberOfBytesSent)
		*lpNumberOfBytesSent = totalBytesSent;
	return (totalBytesToSend == totalBytesSent) ? S_OK : E_FAIL;
}

int WSASendTo(SOCKET socket, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent,
	DWORD dwFlags, const struct sockaddr *lpTo, int iToLen, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
	ULONG totalBytesSent = 0;
	ULONG totalBytesToSend = 0;
	LPWSABUF wsaBuffer = lpBuffers;
	for (DWORD i = 0; i < dwBufferCount; i++)
	{
		int numberOfBytesSent = sendto(socket, wsaBuffer->buf, wsaBuffer->len, dwFlags, lpTo, iToLen);
		if (numberOfBytesSent >= 0)
		{
			if (numberOfBytesSent != wsaBuffer->len)
			{
				::ShowInfoMessage(L"WSASendTo:Number of bytes to be sent=%d. Reportedly sent %d.", wsaBuffer->len, numberOfBytesSent);
			}
			totalBytesSent += numberOfBytesSent;
			totalBytesToSend += wsaBuffer->len;
			wsaBuffer += wsaBuffer->len + sizeof(wsaBuffer->len);
		}
	}
	if (nullptr != lpNumberOfBytesSent)
		*lpNumberOfBytesSent = totalBytesSent;
	return (totalBytesToSend == totalBytesSent) ? S_OK : E_FAIL;
}
#pragma endregion  send, sendto, WSASend, WSASendTo

#endif

